package com.tiancheng.trade.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiancheng.trade.commom.core.enums.MessageTypeEnum;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.data.notify.PayResultMsgBO;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.payment.bo.cmq.RefundResultMsgBO;
import com.tiancheng.trade.payment.bo.notify.PayNotifyBO;
import com.tiancheng.trade.payment.channel.AbstractPayChannel;
import com.tiancheng.trade.payment.channel.PayChannelFactory;
import com.tiancheng.trade.payment.channel.bo.PayResultBO;
import com.tiancheng.trade.payment.channel.bo.RefundResultBO;
import com.tiancheng.trade.payment.channel.paypal.PaypalPayChannel;
import com.tiancheng.trade.payment.channel.ums.UmsConfig;
import com.tiancheng.trade.payment.channel.ums.UmsProperties;
import com.tiancheng.trade.payment.channel.ums.params.UmsQrcNotifyResult;
import com.tiancheng.trade.payment.channel.ums.utils.UmsConstant;
import com.tiancheng.trade.payment.channel.ums.utils.UmsQrcUtil;
import com.tiancheng.trade.payment.channel.umsdhjt.UmsDhjtAsyncComponent;
import com.tiancheng.trade.payment.channel.umsdhjt.request.UmsDhjtSettleNotifyRequest;
import com.tiancheng.trade.payment.convert.NotifyConvert;
import com.tiancheng.trade.payment.enums.FlowStatusEnum;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.payment.enums.UmsReqTypeEnum;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.service.*;
import com.tiancheng.trade.payment.mq.PaymentCenterMsgProducer;
import com.tiancheng.trade.payment.util.cache.PaymentCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 渠道回调处理
 */
@Slf4j
@Service
public class NotifyServiceImpl implements NotifyService {

    @Resource
    private PaymentOrderService paymentOrderService;
    @Resource
    private PaymentCenterMsgProducer paymentCenterMsgProducer;
    @Resource
    private TallyService tallyService;
    @Resource
    private PaymentRefundService paymentRefundService;
    @Resource
    private QrFlowService qrFlowService;
    @Resource
    private UmsConfig umsConfig;
    @Resource
    private PaypalOrderService paypalOrderService;
    @Resource
    private PaypalPayChannel paypalPayChannel;
    @Resource
    private UmsDhjtAsyncComponent umsDhjtAsyncComponent;
    @Resource
    private IPaymentMessageLogService paymentMessageLogService;


    @Override
    public void payNotifyHandle(Map<String, Object> paramsMap, PaymentChannelEnum paymentChannelEnum) throws BusinessException {
        if (paymentChannelEnum.getCode().equals(PaymentChannelEnum.PAYPAL.getCode())) {
            //paypal回调参数解析处理
            PayResultBO payResultBO = paypalPayChannel.payResult(paramsMap);
            //状态不为成功或失败，不做处理
            if (payResultBO.getStatus() == null || "".equals(payResultBO.getStatus())) {
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_UNKNOWN_STATUS.assertIsFalse(false);
            }
            // 修改流水
            IntPaymentOrder intPaymentOrder = paypalOrderService.payResult(payResultBO, "");
            // 发送消息通知订单中心
            PayResultMsgBO payResultMsgBO = new PayResultMsgBO();
            payResultMsgBO.setFlowId(intPaymentOrder.getFlowId());
            payResultMsgBO.setPaymentId(intPaymentOrder.getPaymentId());
            payResultMsgBO.setUserId(intPaymentOrder.getUserId());
            payResultMsgBO.setAmount(intPaymentOrder.getAmount());
            payResultMsgBO.setPayTime(intPaymentOrder.getFlowTime());
            payResultMsgBO.setStatus(payResultBO.getStatus());
            payResultMsgBO.setTransactionId(intPaymentOrder.getTransactionId());
            payResultMsgBO.setTradeNo(intPaymentOrder.getTradeNo());
            payResultMsgBO.setOrderApp(intPaymentOrder.getOrderApp());
            payResultMsgBO.setPaymentChannel(intPaymentOrder.getPaymentChannel());
            payResultMsgBO.setPaymentWay(intPaymentOrder.getPaymentWay());
            payResultMsgBO.setPayScene(intPaymentOrder.getPayScene());
            payResultMsgBO.setCouponAmount(payResultBO.getCouponAmount());
            boolean success = paymentCenterMsgProducer.payResult(payResultMsgBO);

            // 如果消息发送失败则报出异常，controller里面返回失败，让渠道继续推送
            BusinessErrorInfoEnum.MESSAGE_SEND_ERROR.assertIsTrue(success);
            // 保存消息
            saveMessage(payResultMsgBO.getFlowId(),payResultMsgBO.getPaymentChannel(),MessageTypeEnum.order_pay_result,JsonUtil.toJsonString(payResultMsgBO));
        } else {
            AbstractPayChannel payChannel = PayChannelFactory.get(paymentChannelEnum.getCode());
            PayResultBO payResultBO = payChannel.payResult(paramsMap);
            // 修改流水
            final PayNotifyBO paymentOrder = paymentOrderService.payResult(payResultBO);
            if (null != paymentOrder && StringUtils.isNotEmpty(paymentOrder.getFlowId())) {
                boolean success = sendPayFlowMessage(paymentOrder, payResultBO.getStatus(), payResultBO.getCouponAmount());
                if (paymentChannelEnum.getCode().equals(PaymentChannelEnum.CMB.getCode())) {
                    // 招行的订单支付信息推送支付流水到对账中心
                    if ("success".equals(payResultBO.getStatus())) {
                        tallyService.payFlow(payResultBO.getOrderId());
                    }
                }
                if (success) {
                    log.info("渠道侧消息回调处理完成，清空自动查询查询订单缓存！");
                    PaymentCache.delCachePaymentStatusQueryParams(paymentOrder.getFlowId());
                }
            }
        }
    }

    /**
     * 发送支付消息
     *
     * @param paymentOrder 支付流水
     * @param status       支付状态
     * @param couponAmount 优惠金额
     */
    @Override
    public boolean sendPayFlowMessage(PayNotifyBO paymentOrder, String status, Integer couponAmount) {
        // 发送消息通知订单中心
        PayResultMsgBO payResultMsgBO = new PayResultMsgBO();
        payResultMsgBO.setFlowId(paymentOrder.getFlowId());
        payResultMsgBO.setPaymentId(paymentOrder.getPaymentId());
        payResultMsgBO.setUserId(paymentOrder.getUserId());
        payResultMsgBO.setAmount(paymentOrder.getAmount());
        payResultMsgBO.setPayTime(paymentOrder.getPayTime());
        payResultMsgBO.setStatus(status);
        payResultMsgBO.setTransactionId(paymentOrder.getTransactionId());
        payResultMsgBO.setTradeNo(paymentOrder.getTradeNo());
        payResultMsgBO.setOrderApp(paymentOrder.getOrderApp());
        payResultMsgBO.setPaymentChannel(paymentOrder.getPaymentChannel());
        payResultMsgBO.setPaymentWay(paymentOrder.getPaymentWay());
        payResultMsgBO.setPayScene(paymentOrder.getPayScene());
        payResultMsgBO.setCouponAmount(couponAmount);
        payResultMsgBO.setSubFlows(NotifyConvert.INSTANCE.convert(paymentOrder.getSubFlows()));
        boolean success = paymentCenterMsgProducer.payResult(payResultMsgBO);
        // 如果消息发送失败则报出异常，controller里面返回失败，让渠道继续推送
        BusinessErrorInfoEnum.MESSAGE_SEND_ERROR.assertIsTrue(success);
        // 保存消息
        saveMessage(payResultMsgBO.getFlowId(),payResultMsgBO.getPaymentChannel(),MessageTypeEnum.order_pay_result,JsonUtil.toJsonString(payResultMsgBO));
        return success;
    }

    @Override
    public void refundNotifyHandle(Map<String, Object> paramsMap, PaymentChannelEnum paymentChannelEnum) throws BusinessException {
        if (paymentChannelEnum.getCode().equals(PaymentChannelEnum.PAYPAL.getCode())) {
            //获取退款结果
            RefundResultBO refundResultBO = paypalPayChannel.refundResult(paramsMap);
            //状态不为成功或失败，不做处理
            if (refundResultBO.getStatus() == null || "".equals(refundResultBO.getStatus())) {
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_UNKNOWN_STATUS.assertIsFalse(false);
            }
            //退款结果处理
            IntRefundFlow intRefundFlow = paypalOrderService.refundResult(refundResultBO, "");
            // 发送退款消息通知订单中心
            if (intRefundFlow.getRefundStatus().equals(FlowStatusEnum.PART_SUCCESS.getCode())) {
                // 部分退款成功回调
                log.info("部分退款回调成功");
                return;
            }
            RefundResultMsgBO msgBO = new RefundResultMsgBO();
            msgBO.setPaymentId(intRefundFlow.getPaymentId());//退款单号
            msgBO.setFlowId(intRefundFlow.getFlowId());
            msgBO.setUserId(intRefundFlow.getUserId());
            msgBO.setAmount(intRefundFlow.getAmount());
            msgBO.setRefundTime(intRefundFlow.getFlowTime());
            msgBO.setStatus(refundResultBO.getStatus());
            msgBO.setOrderApp(intRefundFlow.getOrderApp());
            msgBO.setPaymentChannel(intRefundFlow.getPaymentChannel());
            msgBO.setPaymentWay(intRefundFlow.getPaymentWay());
            msgBO.setTradeNo(intRefundFlow.getTradeNo());
            msgBO.setRefundAmount(intRefundFlow.getRefundAmount());
            msgBO.setPayFlowId(intRefundFlow.getPayFlowId());
            boolean success = paymentCenterMsgProducer.refundResult(msgBO);
            // 如果消息发送失败则报出异常，controller里面返回失败，让渠道继续推送
            BusinessErrorInfoEnum.MESSAGE_SEND_ERROR.assertIsTrue(success);
            // 保存消息
            saveMessage(msgBO.getFlowId(),msgBO.getPaymentChannel(),MessageTypeEnum.order_refund_result,JsonUtil.toJsonString(msgBO));

        } else {
            AbstractPayChannel payChannel = PayChannelFactory.get(paymentChannelEnum.getCode());
            RefundResultBO refundResultBO = payChannel.refundResult(paramsMap);
            if (null != refundResultBO) {
                refundResultBO.setPaymentChannel(paymentChannelEnum);
            }
            PaymentRefundFlow refundFlow = null;
            if (paymentChannelEnum.getCode().equals(PaymentChannelEnum.APPLE.getCode())) {
                if (null == refundResultBO) {
                    return;
                }
                // 苹果的退款回调，不需要再次修改退款单状态
                refundFlow = paymentRefundService.findByFlowId(refundResultBO.getRefundId());
            } else {
                refundFlow = paymentRefundService.refundResult(refundResultBO);
                // 发送退款消息通知订单中心
                if (refundFlow.getRefundStatus().equals(FlowStatusEnum.PART_SUCCESS.getCode())) {
                    // 部分退款成功回调
                    log.info("部分退款回调成功");
                    return;
                }
            }
            RefundResultMsgBO msgBO = new RefundResultMsgBO();
            msgBO.setPaymentId(refundFlow.getPaymentId());
            msgBO.setFlowId(refundFlow.getFlowId());
            msgBO.setUserId(refundFlow.getUserId());
            msgBO.setAmount(refundFlow.getAmount());
            msgBO.setRefundTime(refundFlow.getFlowTime());
            msgBO.setStatus(refundResultBO.getStatus());
            msgBO.setOrderApp(refundFlow.getOrderApp());
            msgBO.setPaymentChannel(refundFlow.getPaymentChannel());
            msgBO.setPaymentWay(refundFlow.getPaymentWay());
            msgBO.setTradeNo(refundFlow.getTradeNo());
            msgBO.setRefundAmount(refundFlow.getRefundAmount());
            msgBO.setPayFlowId(refundFlow.getPayFlowId());
            boolean success = paymentCenterMsgProducer.refundResult(msgBO);
            // 如果消息发送失败则报出异常，controller里面返回失败，让渠道继续推送
            BusinessErrorInfoEnum.MESSAGE_SEND_ERROR.assertIsTrue(success);
            if (paymentChannelEnum.getCode().equals(PaymentChannelEnum.CMB.getCode())) {
                // 招行的订单退款信息推送退款流水到对账中心
                if ("success".equals(refundResultBO.getStatus())) {
                    tallyService.refundFlow(refundResultBO.getRefundId());
                }
            }
            // 保存消息
            saveMessage(msgBO.getFlowId(),msgBO.getPaymentChannel(),MessageTypeEnum.order_refund_result,JsonUtil.toJsonString(msgBO));
        }
    }

    @Override
    public Map<String, String> qrC2BNotifyHandle(Map<String, String> paramsMap) {
        if (paramsMap == null || paramsMap.size() == 0) {
            log.error("C2B交易通知-返回数据为空");
            return null;
        }

        final String reqType = MapUtils.getString(paramsMap, "reqType");
        final UmsReqTypeEnum byCode = UmsReqTypeEnum.getByCode(reqType);

        if (byCode.getCode().equals(UmsReqTypeEnum.C2B_ADDN_RECEIVE.getCode())) {
            // 附加处理请求
            return qrFlowService.qrC2BAddnReceive(paramsMap);
        }
        Long orgPaymentChannelId = null;
        if (byCode.getCode().equals(UmsReqTypeEnum.C2B_TRADE_NOTIFY.getCode())) {
            // 交易通知
            String code = "00";
            String msg = "成功";
            try {
                if (!UmsQrcUtil.validate(paramsMap, UmsConstant.ENCODING)) {
                    log.error("C2B交易通知-验证签名结果[失败]");
                    //验签失败，需解决验签问题
                    code = "11";
                    msg = "验证签名失败";
                } else {
                    final UmsQrcNotifyResult message = JSON.parseObject(JSON.toJSONString(paramsMap, SerializerFeature.WriteMapNullValue), UmsQrcNotifyResult.class);
                    final UmsReqTypeEnum origReqTypeEnum = UmsReqTypeEnum.getByCode(message.getOrigReqType());

                    QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
                    //conditions.eq("flow_id", message.getOrderNo());
                    conditions.eq("qr_no", message.getQrNo());
                    final QrFlow qrFlow = qrFlowService.getBaseMapper().selectOne(conditions);
                    orgPaymentChannelId = qrFlow.getOrgPaymentChannelId();
                    switch (origReqTypeEnum) {
                        case C2B_CONSUMER:
                            // 消费交易——支付结果
                            log.info("C2B交易通知-支付消息, OrigReqType:{}", message.getOrigReqType());
                            qrFlowService.qrC2BPayNotify(message);
                            break;
                        case C2B_CONSUMER_RIGHTING:
                            // 消费冲正交易
                            log.info("C2B交易通知-消费冲正交易消息, OrigReqType:{}", message.getOrigReqType());
                            qrFlowService.qrC2BRightingNotify(message);
                            break;
                        case C2B_CONSUMER_REVOKE:
                            // 消费撤销交易
                            log.info("C2B交易通知-消费撤销交易消息, OrigReqType:{}", message.getOrigReqType());
                            qrFlowService.qrC2BRevokeNotify(message);
                            break;
                        case C2B_CONSUMER_REFUND:
                            // 退货交易
                            log.info("C2B交易通知-退货交易消息, OrigReqType:{}", message.getOrigReqType());
                            qrFlowService.qrC2BRefundNotify(message);
                            break;
                        default:
                            log.error("C2B交易通知-未知的通知消息,原交易类型：{}", message.getOrigReqType());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("C2B交易通知处理异常：", e);
                code = "38";
                msg = "基于风险控制原因失败";
            }
            final UmsProperties umsProperties = umsConfig.getByOrgPayChannelId(orgPaymentChannelId);
            Map<String, String> respMap = new HashMap<String, String>();
            respMap.put("reqType", "0250000903");// 固定
            respMap.put("version", umsProperties.getQrcProperties().getVersion());
            respMap.put("respCode", code);
            respMap.put("respMsg", msg);
            return UmsQrcUtil.sign(respMap, umsProperties);
        }
        final UmsProperties umsProperties = umsConfig.getByOrgPayChannelId(orgPaymentChannelId);
        log.warn("银联二维码支付，接收到C2B交易通知，未知的请求类型:{},params:{}", reqType, paramsMap);
        Map<String, String> respMap = new HashMap<String, String>();
        respMap.put("reqType", "0250000903");// 固定
        respMap.put("version", umsProperties.getQrcProperties().getVersion());
        respMap.put("respCode", "38");
        respMap.put("respMsg", "未知的消息类型");
        return UmsQrcUtil.sign(respMap, umsProperties);
    }

    /**
     * 模拟支付成功通知
     *
     * @param payResultBO
     */
    @Async("asyncTaskExecutor")
    public void mockPayNotify(PayResultBO payResultBO) {
        // 修改流水
        final PayNotifyBO paymentOrder = paymentOrderService.payResult(payResultBO);
        sendPayFlowMessage(paymentOrder, payResultBO.getStatus(), payResultBO.getCouponAmount());
    }


    /**
     * 模拟退款成功通知
     *
     * @param refundResultBO
     */
    @Async("asyncTaskExecutor")
    public void mockRefundNotify(RefundResultBO refundResultBO) {
        // 修改流水
        if (null != refundResultBO) {
            refundResultBO.setPaymentChannel(PaymentChannelEnum.UMS);
        }
        PaymentRefundFlow refundFlow = paymentRefundService.refundResult(refundResultBO);
        // 发送退款消息通知订单中心
        if (refundFlow.getRefundStatus().equals(FlowStatusEnum.PART_SUCCESS.getCode())) {
            // 部分退款成功回调
            log.info("部分退款回调成功");
            return;
        }
        RefundResultMsgBO msgBO = new RefundResultMsgBO();
        msgBO.setPaymentId(refundFlow.getPaymentId());
        msgBO.setFlowId(refundFlow.getFlowId());
        msgBO.setUserId(refundFlow.getUserId());
        msgBO.setAmount(refundFlow.getAmount());
        msgBO.setRefundTime(refundFlow.getFlowTime());
        msgBO.setStatus(refundResultBO.getStatus());
        msgBO.setOrderApp(refundFlow.getOrderApp());
        msgBO.setPaymentChannel(refundFlow.getPaymentChannel());
        msgBO.setPaymentWay(refundFlow.getPaymentWay());
        msgBO.setTradeNo(refundFlow.getTradeNo());
        msgBO.setRefundAmount(refundFlow.getRefundAmount());
        msgBO.setPayFlowId(refundFlow.getPayFlowId());
        boolean success = paymentCenterMsgProducer.refundResult(msgBO);
        // 如果消息发送失败则报出异常，controller里面返回失败，让渠道继续推送
        BusinessErrorInfoEnum.MESSAGE_SEND_ERROR.assertIsTrue(success);

        // 保存消息
        saveMessage(refundFlow.getFlowId(),refundFlow.getPaymentChannel(),MessageTypeEnum.order_refund_result,JsonUtil.toJsonString(msgBO));
    }

    /**
     * 大华接通结算通知消息
     *
     * @param params
     */
    @Override
    public void dhjtSettleResult(UmsDhjtSettleNotifyRequest params) {
        // 1、处理结算结果
        BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(null != params.getData(), "消息内容为空！");
        UmsDhjtSettleNotifyRequest.UmsDhjtSettleNotifyBody data = params.getData();
        final String batchNo = data.getBatchNo();
        // 2、通知订单中心，更新账单信息
    }


    private void saveMessage(String flowId, String paymentChannel, MessageTypeEnum messageTypeEnum, String messageBody) {
        // 推送消息记录到数据库
        try {
            PaymentMessageLog paymentMessageLog = new PaymentMessageLog();
            paymentMessageLog.setMessageType(messageTypeEnum.getCode());
            paymentMessageLog.setFlowId(flowId);
            paymentMessageLog.setPaymentChannel(paymentChannel);
            paymentMessageLog.setCallbackStatus("1");
            paymentMessageLog.setMessageBody(messageBody);
            paymentMessageLog.setCreatedDt(LocalDateTime.now());
            paymentMessageLogService.save(paymentMessageLog);
        } catch (Exception e) {
            log.error("保存推送消息异常:", e);
        }

    }
}
